package com.mzq.langchain4j_springboot.config;

import com.mzq.langchain4j_springboot.web.service.Impl.*;
import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.document.DocumentSplitter;
import dev.langchain4j.data.document.loader.ClassPathDocumentLoader;
import dev.langchain4j.data.document.loader.FileSystemDocumentLoader;
import dev.langchain4j.data.document.parser.TextDocumentParser;
import dev.langchain4j.data.document.parser.apache.tika.ApacheTikaDocumentParser;
import dev.langchain4j.data.document.splitter.DocumentSplitters;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.memory.ChatMemory;
import dev.langchain4j.memory.chat.ChatMemoryProvider;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;

import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.chat.StreamingChatModel;
import dev.langchain4j.model.ollama.OllamaEmbeddingModel;
import dev.langchain4j.rag.content.retriever.ContentRetriever;
import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever;
import dev.langchain4j.service.*;
import dev.langchain4j.store.embedding.EmbeddingStore;
import dev.langchain4j.store.embedding.EmbeddingStoreIngestor;
import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore;
import dev.langchain4j.store.embedding.milvus.MilvusEmbeddingStore;
import io.milvus.client.MilvusServiceClient;
import io.milvus.common.clientenum.ConsistencyLevelEnum;
import io.milvus.param.IndexType;
import io.milvus.param.MetricType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import reactor.core.publisher.Flux;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;

@Configuration
public class AiConfig {
//    @Autowired
//    private RedisEmbeddingStore redisEmbeddingStore;
    //这里主要是为了配置对话记录作用的 对应ChatController中的chats 那个方法太麻烦了  保存之前对话 要不跟模型对话 总是说第二句忘记第一句
    public interface Assistant {
        String chat(dev.langchain4j.data.message.UserMessage message);

        // 流式响应
        TokenStream stream(String message);

        //预设角色 有点像dify中填入的systemmessage那里  提示词需要不断调整
        @SystemMessage("""
                您是“天津能源投资集团”航空公司的客户聊天支持代理。请以友好、乐于助人且愉快的方式来回复。
                        您正在通过在线聊天系统与客户互动。
                        在提供有关预订或取消预订的信息之前，您必须始终从用户处获取以下信息：预订号、客户姓名。
                        请讲中文。
					   今天的日期是 {{current_date}}.
                """)
        TokenStream stream(@UserMessage String message,@V("current_date") String currentDate);
    }

    //这里是没有fuction-call的方法
//    @Bean
//    public Assistant assistant(ChatLanguageModel qwenChatModel,
//                               StreamingChatLanguageModel qwenStreamingChatModel) {
//        ChatMemory chatMemory = MessageWindowChatMemory.withMaxMessages(10);
//
//        //主要通过代理获取
//        return AiServices.builder(Assistant.class)
//                .chatLanguageModel(qwenChatModel)
//                .streamingChatLanguageModel(qwenStreamingChatModel)
//                .chatMemory(chatMemory)
//                .build();
//    }

//    @Bean
//    public Assistant assistant(ChatLanguageModel qwenChatModel,
//                               StreamingChatLanguageModel qwenStreamingChatModel,SysUserServiceImpl toolsService) {
//        ChatMemory chatMemory = MessageWindowChatMemory.withMaxMessages(10);
//
//        //主要通过代理获取
//        return AiServices.builder(Assistant.class)
//                .chatLanguageModel(qwenChatModel)
//                .streamingChatLanguageModel(qwenStreamingChatModel)
//                //fuction-call重点参数
//                .tools(toolsService)
//                .chatMemory(chatMemory)
//                .build();
//    }

    //这里是为了区分不同用户或者不同对话 不能用同一个记忆 其实后续也就可以完成对用户角色权限划分
    public interface AssistantUnique {

        @SystemMessage(fromResource = "tdfc-prompt.text")
        String chat(@UserMessage String userMessage);

        //流式响应（使用默认系统提示词，通过 @MemoryId 实现会话隔离）
        Flux<String> stream(@MemoryId int memoryId, @UserMessage String userMessage);
        //流式响应（明确指定使用 system-prompt.txt 文件中的系统提示词，同样支持会话隔离）
        @SystemMessage(fromResource = "system-prompt.txt")
        Flux<String> chatStream(@MemoryId int memoryId, @UserMessage String userMessage);
    }

//    @Bean
//    public AssistantUnique assistantUnique(ChatLanguageModel qwenChatModel,
//                                           StreamingChatLanguageModel qwenStreamingChatModel) {
//
//        AssistantUnique assistant = AiServices.builder(AssistantUnique.class)
//                .chatLanguageModel(qwenChatModel)
//                .streamingChatLanguageModel(qwenStreamingChatModel)
//                .chatMemoryProvider(memoryId ->
//                        MessageWindowChatMemory.builder().maxMessages(10)
//                                .id(memoryId).build()
//                )
//                .build();
//        return assistant;
//    }


//    //这里是为了持久化到数据库
//    @Bean
//    public AssistantUnique assistantUniqueStore(ChatLanguageModel qwenChatModel,
//                                                StreamingChatLanguageModel qwenStreamingChatModel) {
//
//        PersistentChatMemoryStore store = new PersistentChatMemoryStore();
//        System.out.println("store======>"+store);
//        ChatMemoryProvider chatMemoryProvider = memoryId -> MessageWindowChatMemory.builder()
//                .id(memoryId)
//                .maxMessages(10)
//                .chatMemoryStore(store)
//                .build();
//
//        AssistantUnique assistant = AiServices.builder(AssistantUnique.class)
//                .chatLanguageModel(qwenChatModel)
//                .streamingChatLanguageModel(qwenStreamingChatModel)
//                .chatMemoryProvider(memoryId ->
//                        MessageWindowChatMemory.builder().maxMessages(10)
//                                .id(memoryId).build()
//                )
//                .chatMemoryProvider(chatMemoryProvider)
//                .build();
//        return assistant;
//    }

    //这里是为了调用fuction-call  识别什么问题调用业务系统的 SysUserServiceImpl
    @Bean
    public AssistantUnique assistantUniqueStore(ChatModel qwenChatModel,
                                                StreamingChatModel qwenStreamingChatModel,
                                                HouseInfoServiceImpl houseInfoService,
                                                RentInfoServiceImpl rentInfoService,
                                                BusinessInspectionServiceImpl businessInspectionService,
                                                MaintenanceRecordServiceImpl maintenanceRecordService,
                                                SysUserServiceImpl toolsService,
//                                                PersistentChatMemoryStore store,
                                                EmbeddingStore embeddingStore,
                                                OllamaEmbeddingModel qwenEmbeddingModel) {

        // 向量化文档存入数据库 (文档加载 -> 文档切割 -> 文本向量化 -> 存入EmbeddingStore)
        // 1. 加载目录下所有文档
        List<Document> documents = ClassPathDocumentLoader.loadDocuments(
                "rag",
                new ApacheTikaDocumentParser()
        );

        // 2. 文档切割，每个文档按每段进行分割，最大 1000 字符，每次重叠最多 200 个字符
        DocumentSplitter splitter = DocumentSplitters.recursive(1000, 200);

        // 3. 构建文档加载器
        EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder()
                // 为了提高搜索质量，为每个 TextSegment 添加文档名称
                .textSegmentTransformer(textSegment -> TextSegment.from(
                        textSegment.metadata().getString("file_name") + "\n" + textSegment.text(),
                        textSegment.metadata()
                ))
                .embeddingModel(qwenEmbeddingModel)
                .embeddingStore(embeddingStore)
                .documentSplitter(splitter)
                .build();

        // 4. 执行向量化并存储到 Milvus
        ingestor.ingest(documents);

        // 配置RAG检索器(用户问题 → 向量化 → 检索相似片段)
        ContentRetriever contentRetriever = EmbeddingStoreContentRetriever.builder()
                .embeddingStore(embeddingStore)
                .embeddingModel(qwenEmbeddingModel)
                .maxResults(5) // 最相似的5个结果
                .minScore(0.6) // 只找相似度在0.6以上的内容
                .build();

//        //配置持久化聊天记忆
//        System.out.println("store======>"+store);
//        ChatMemoryProvider chatMemoryProvider = memoryId -> MessageWindowChatMemory.builder()
//                .id(memoryId)
//                .maxMessages(10)
//                .chatMemoryStore(store)
//                .build();

        //构建AI Service
        AssistantUnique assistant = AiServices.builder(AssistantUnique.class)
                .chatModel(qwenChatModel)
                .streamingChatModel(qwenStreamingChatModel)
                .tools(rentInfoService)
                .tools(businessInspectionService)
                .tools(maintenanceRecordService)
                .tools(houseInfoService)
                .tools(toolsService)
                .chatMemoryProvider(memoryId ->
                        MessageWindowChatMemory.builder().maxMessages(10)
                                .id(memoryId).build()
                )
//                .chatMemoryProvider(chatMemoryProvider)
                .contentRetriever(contentRetriever)
                .build();
        return assistant;
    }

    //定义向量数据库  InMemoryEmbeddingStore
    @Bean
    public EmbeddingStore embeddingStore(){
        return MilvusEmbeddingStore.builder()
                .host("localhost")
                .port(19530)
                .collectionName("langchain4j_collection") // 集合
                .dimension(1024) // 向量维度
                .indexType(IndexType.FLAT) // 索引类型
                .metricType(MetricType.COSINE)
                .consistencyLevel(ConsistencyLevelEnum.EVENTUALLY)
                .autoFlushOnInsert(true)
                .idFieldName("id")
                .textFieldName("text")
                .metadataFieldName("metadata")
                .vectorFieldName("vector")
                .build();
    }
    /**
     * 构建向量数据库操作对象
     */
//    @Bean
//    public EmbeddingStore store(OllamaEmbeddingModel embeddingModel){
//        // 加载文档进内存
//        // List<Document> documents = ClassPathDocumentLoader.loadDocuments("content");
//        List<Document> documents = ClassPathDocumentLoader.loadDocuments("rag", new TextDocumentParser());
//        // List<Document> documents = FileSystemDocumentLoader.loadDocuments("D:\\back\\spring-agent\\src\\main\\resources\\content");
//        // 构建向量数据库操作对象,操作的是内存版本的向量数据库
//        // InMemoryEmbeddingStore store = new InMemoryEmbeddingStore();
//
//        // 构建文档分割器对象
//        DocumentSplitter ds = DocumentSplitters.recursive(500, 100);
//
//        // 构建EmbeddingStoreIngestor对象，完成文本数据切割、向量化、存储
//        EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder()
//                //.embeddingStore(store)
//                .embeddingStore(redisEmbeddingStore)
//                .documentSplitter(ds)
//                .embeddingModel(embeddingModel)
//                .build();
//        ingestor.ingest(documents);
//        return redisEmbeddingStore;
//    }

    // 没有独立会话
    @Bean
    public Assistant assistant(ChatModel qwenChatModel,
                               StreamingChatModel qwenStreamingChatModel,
                               ToolsService toolsService,
                               EmbeddingStore embeddingStore,
                               OllamaEmbeddingModel qwenEmbeddingModel) {
        ChatMemory chatMemory = MessageWindowChatMemory.withMaxMessages(10);


        ContentRetriever contentRetriever = EmbeddingStoreContentRetriever.builder()
                .embeddingStore(embeddingStore)
                .embeddingModel(qwenEmbeddingModel)
                .maxResults(5) // 最相似的5个结果
                .minScore(0.6) // 只找相似度在0.6以上的内容
                .build();

        Assistant assistant = AiServices.builder(Assistant.class)
                .chatModel(qwenChatModel)
                .streamingChatModel(qwenStreamingChatModel)
                .tools(toolsService)
                .contentRetriever(contentRetriever)
                .chatMemory(chatMemory)
                .build();

        return  assistant;
    }

    //这里是为了定义股权小助手
    public interface EquityAssistant {
        String chat(String message);

        // 流式响应
        TokenStream stream(String message);

        //预设角色 有点像dify中填入的systemmessage那里  提示词需要不断调整
//        @SystemMessage("""
//                您是天津能源投资集团科技有限公司的股权分析小助手，请以友好、乐于助人且愉快的方式来回复，
//                您正在通过线下聊天系统与客户互动，请讲中文，今天的日期是 {{current_date}}。通过查询到的数据，
//                对输出结果进行归因分析，并展示分析结果，最后根据返回结果判断能否生成echarts代码，如果能，自动生成echarts可执行代码
//                """)
//        TokenStream stream(@UserMessage String message,@V("current_date") String currentDate);
        @SystemMessage(fromResource = "system-prompt.txt")
        TokenStream stream(@UserMessage String message,@V("current_date") String currentDate);
        @SystemMessage("""
                你是一个隐患排查小助手，可以根据图片信息，查询是否存在违规内容，并提出合理建议
                """)
        TokenStream streamByRL(@UserMessage String message, @V("current_date") String currentDatet);
    }

    @Bean
    public EquityAssistant equityAssistant(ChatModel qwenChatModel,
                               StreamingChatModel qwenStreamingChatModel,
                               ToolsService toolsService,
                               EmbeddingStore embeddingStore,
                               OllamaEmbeddingModel qwenEmbeddingModel) {
        ChatMemory chatMemory = MessageWindowChatMemory.withMaxMessages(4096);


//        ContentRetriever contentRetriever = EmbeddingStoreContentRetriever.builder()
//                .embeddingStore(embeddingStore)
//                .embeddingModel(qwenEmbeddingModel)
//                .maxResults(5) // 最相似的5个结果
//                .minScore(0.6) // 只找相似度在0.6以上的内容
//                .build();

        EquityAssistant assistant = AiServices.builder(EquityAssistant.class)
                .chatModel(qwenChatModel)
                .streamingChatModel(qwenStreamingChatModel)
//                .tools(toolsService)
//                .contentRetriever(contentRetriever)
                .chatMemory(chatMemory)
                .build();

        return  assistant;
    }
}